export namespace ui {
  export type Style = Partial<CSSStyleDeclaration>

  export interface CommonElementProps {
    innerHTML?: string;
    className?: string;
    style?: Style;
    id?: string;
    onclick?: (e: MouseEvent) => void;
    children?: UINode[] | UINode;
  }
  export interface ElementPropsTagNameMap {
    "a": CommonElementProps;
    "abbr": CommonElementProps;
    "address": CommonElementProps;
    "area": CommonElementProps;
    "article": CommonElementProps;
    "aside": CommonElementProps;
    "audio": CommonElementProps;
    "b": CommonElementProps;
    "base": CommonElementProps;
    "bdi": CommonElementProps;
    "bdo": CommonElementProps;
    "blockquote": CommonElementProps;
    "body": CommonElementProps;
    "br": CommonElementProps;
    "button": CommonElementProps & { disabled?: boolean };
    "canvas": CommonElementProps;
    "caption": CommonElementProps;
    "cite": CommonElementProps;
    "code": CommonElementProps;
    "col": CommonElementProps;
    "colgroup": CommonElementProps;
    "data": CommonElementProps;
    "datalist": CommonElementProps;
    "dd": CommonElementProps;
    "del": CommonElementProps;
    "details": CommonElementProps;
    "dfn": CommonElementProps;
    "dialog": CommonElementProps;
    "div": CommonElementProps;
    "dl": CommonElementProps;
    "dt": CommonElementProps;
    "em": CommonElementProps;
    "embed": CommonElementProps;
    "fieldset": CommonElementProps;
    "figcaption": CommonElementProps;
    "figure": CommonElementProps;
    "footer": CommonElementProps;
    "form": CommonElementProps;
    "h1": CommonElementProps;
    "h2": CommonElementProps;
    "h3": CommonElementProps;
    "h4": CommonElementProps;
    "h5": CommonElementProps;
    "h6": CommonElementProps;
    "head": CommonElementProps;
    "header": CommonElementProps;
    "hgroup": CommonElementProps;
    "hr": CommonElementProps;
    "html": CommonElementProps;
    "i": CommonElementProps;
    "iframe": CommonElementProps;
    "img": CommonElementProps & { src?: string };
    "input": CommonElementProps;
    "ins": CommonElementProps;
    "kbd": CommonElementProps;
    "label": CommonElementProps;
    "legend": CommonElementProps;
    "li": CommonElementProps;
    "link": CommonElementProps;
    "main": CommonElementProps;
    "map": CommonElementProps;
    "mark": CommonElementProps;
    "menu": CommonElementProps;
    "meta": CommonElementProps;
    "meter": CommonElementProps;
    "nav": CommonElementProps;
    "noscript": CommonElementProps;
    "object": CommonElementProps;
    "ol": CommonElementProps;
    "optgroup": CommonElementProps;
    "option": CommonElementProps;
    "output": CommonElementProps;
    "p": CommonElementProps;
    "picture": CommonElementProps;
    "pre": CommonElementProps;
    "progress": CommonElementProps;
    "q": CommonElementProps;
    "rp": CommonElementProps;
    "rt": CommonElementProps;
    "ruby": CommonElementProps;
    "s": CommonElementProps;
    "samp": CommonElementProps;
    "script": CommonElementProps;
    "section": CommonElementProps;
    "select": CommonElementProps;
    "slot": CommonElementProps;
    "small": CommonElementProps;
    "source": CommonElementProps;
    "span": CommonElementProps;
    "strong": CommonElementProps;
    "style": CommonElementProps;
    "sub": CommonElementProps;
    "summary": CommonElementProps;
    "sup": CommonElementProps;
    "table": CommonElementProps;
    "tbody": CommonElementProps;
    "td": CommonElementProps;
    "template": CommonElementProps;
    "textarea": CommonElementProps;
    "tfoot": CommonElementProps;
    "th": CommonElementProps;
    "thead": CommonElementProps;
    "time": CommonElementProps;
    "title": CommonElementProps;
    "tr": CommonElementProps;
    "track": CommonElementProps;
    "u": CommonElementProps;
    "ul": CommonElementProps;
    "var": CommonElementProps;
    "video": CommonElementProps;
    "wbr": CommonElementProps;
  }
  export type UINode = Node | undefined | string | null;
  export type UIFunc<P> = (props: P) => UINode;
  export function node<P>(tagName: UIFunc<P>, props: P, children?: UINode[] | UINode): UINode;
  export function node<K extends keyof ElementPropsTagNameMap>(tagName: K, props: ElementPropsTagNameMap[K], children?: UINode[] | UINode): HTMLElementTagNameMap[K];
  export function node<K extends keyof HTMLElementTagNameMap, P = any>(tagName: UIFunc<P> | K, props: any, children?: UINode[] | UINode): UINode {
    if (typeof tagName === 'string') {
      const ret = document.createElement(tagName);
      if (props) {
        if (props.style) {
          applyStyle(ret, props.style);
          delete props.style;
        }
        Object.assign(ret, props);
      }
      const _children = props.children || children;
      if (typeof _children === 'string') {
        ret.innerText = _children;
      } else if (Array.isArray(_children)) {
        for (let i = 0; i < _children.length; ++i) {
          const child = _children[i]
          if (!child) { continue; }
          if (typeof child === 'string') {
            ret.innerText += child
          } else {
            ret.appendChild(child)
          }
        }
      }
      return ret;
    } else {
      const ret = tagName(props);
      return ret;
    }
  }
  export function applyStyle(ele: HTMLElement, style: Style) {
    ele.setAttribute('style', '')
    Object.assign(ele.style, style);
  }
}